import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.picking.behaviors.*;
import com.sun.j3d.utils.behaviors.keyboard.*;
import javax.swing.JFrame;


/**
* Ein blauer Wuerfel kann durch Anklicken mit der rechten Maustaste bewegt werden.
* In der Szene befindet sich ausserdem zwei weitere Objekte:
* Eine gruene Kugel, die ihre Farbe auf rot wechselt, wenn der Wuerfel mit
* ihr kollidiert.
* Ein goldener Zylinder, der sich bewegt, wenn er vom Wuerfel beruehrt wird.
*
* Ausserdem wird hier die Navigation mittels Cursor-Tasten ermoeglicht.
*
* @author Frank Klawonn
* Letzte Aenderung 07.07.2005
* @see CollisionBehaviour1
* @see CollisionBehaviour2
*/
public class CollisionExample extends JFrame
{

  //Der Canvas, auf den gezeichnet wird.
  public Canvas3D myCanvas3D;


  public CollisionExample()
  {
    //Mechanismus zum Schliessen des Fensters und beenden des Programms
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


    //Standardeinstellung fuer das Betrachteruniversum
    myCanvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration());


    //Aufbau des SimpleUniverse:
    //Zuerst Erzeugen zusammen mit dem Canvas
    SimpleUniverse simpUniv = new SimpleUniverse(myCanvas3D);


    //Standardpositionierung des Betrachters
    simpUniv.getViewingPlatform().setNominalViewingTransform();


    //Die Szene wird in dieser Methode erzeugt.
    createSceneGraph(simpUniv);


    //Hinzufuegen von Licht
    addLight(simpUniv);

    //Hierdurch kann man mit der Maus den Betrachterstandpunkt veraendern


    //Darstellung des Canvas/Fensters:
    setTitle("Bewegen Sie den Wuerfel mit der rechten Maustaste.");
    setSize(700,700);
    getContentPane().add("Center", myCanvas3D);
    setVisible(true);

  }




  public static void main(String[] args)
  {
     CollisionExample colliexam = new CollisionExample();
  }





  //In dieser Methode werden die Objekte der Szene aufgebaut und dem
  //SimpleUniverse su hinzugefuegt.
  public void createSceneGraph(SimpleUniverse su)
  {

    //Eine blaue Appearance fuer den Wuerfel.
    Color3f ambientColourBox = new Color3f(0.0f,0.0f,0.7f);
    Color3f emissiveColourBox = new Color3f(0.0f,0.0f,0.0f);
    Color3f diffuseColourBox = new Color3f(0.0f,0.0f,0.7f);
    Color3f specularColourBox = new Color3f(0.0f,0.0f,0.8f);
    float shininessBox = 128.0f;

    Appearance boxApp = new Appearance();

    boxApp.setMaterial(new Material(ambientColourBox,emissiveColourBox,
                           diffuseColourBox,specularColourBox,shininessBox));


    //Erzeuge den Wuerfel
    float cubeHalfLength = 0.1f;
    Box moveBox = new Box(cubeHalfLength,cubeHalfLength,cubeHalfLength,boxApp);


    //Die CollisionBounds werden fuer den Wuerfel
    moveBox.setCollisionBounds(new BoundingBox(new Point3d(0.0,0.0,0.0),
                                               new Point3d(cubeHalfLength,
                                                           cubeHalfLength,
                                                           cubeHalfLength)));

    moveBox.setCollidable(true);

    //Positionierung des Wuerfels und Zurodnung zu seiner Transformationsgruppe
    Transform3D tfBox = new Transform3D();
    tfBox.rotY(Math.PI/6);
    Transform3D rotationX = new Transform3D();
    rotationX.rotX(-Math.PI/5);
    tfBox.mul(rotationX);
    TransformGroup tgBox = new TransformGroup(tfBox);
    tgBox.addChild(moveBox);


    //Diese Eigenschaften werden benoetigt, um den Wuerfel bewegen zu koennen.
    tgBox.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    tgBox.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
    tgBox.setCapability(TransformGroup.ENABLE_PICK_REPORTING);



    //Erzeuge eine goldene Appearance fuer den Zylinder.
    Color3f ambientColourCylinder = new Color3f(0.0f,0.0f,0.0f);
    Color3f emissiveColourCylinder = new Color3f(0.0f,0.0f,0.0f);
    Color3f diffuseColourCylinder = new Color3f(0.8f,0.4f,0.0f);
    Color3f specularColourCylinder = new Color3f(0.8f,0.8f,0.0f);
    float shininessCylinder = 100.0f;

    Appearance yellowCylinderApp = new Appearance();

    yellowCylinderApp.setMaterial(new Material(ambientColourCylinder,
                                               emissiveColourCylinder,
                                               diffuseColourCylinder,
                                               specularColourCylinder,
                                               shininessCylinder));


    //Erzeuge den Zylinder.
    Cylinder cyli = new Cylinder(0.1f,0.3f,yellowCylinderApp);

    //Die CollisionBounds fuer den Zylinder
    cyli.setCollisionBounds(new BoundingBox(new Point3d(0.0,-0.15,0.0),
                                            new Point3d(0.1,0.15,0.1)));
    cyli.setCollidable(true);

    //Positionierung des Zylinders und Zuordnung zu seiner Transformationsgruppe
    Transform3D tfCylinder = new Transform3D();
    tfCylinder.mul(rotationX);
    Transform3D positionCyl = new Transform3D();
    positionCyl.setTranslation(new Vector3f(-0.7f,0.0f,0.0f));
    tfCylinder.mul(positionCyl);

    TransformGroup tgCylinder = new TransformGroup(tfCylinder);
    tgCylinder.addChild(cyli);

    //Diese Transformgruppe wird fuer die Bewegung des Zylinder benoetigt.
    TransformGroup tgmCyl = new TransformGroup();
    tgmCyl.addChild(tgCylinder);

    //Die Bewegung von links nach rechts
    Transform3D escape = new Transform3D();
    Alpha cylAlphaR = new Alpha(1,2000);

    //Die Startzeit muss zunaechst ins Unendliche verschoben werden:
    cylAlphaR.setStartTime(Long.MAX_VALUE);

    //Die Bewegung
    float maxRight = 0.5f;
    PositionInterpolator cylMoveR = new PositionInterpolator(cylAlphaR,tgmCyl,
                                                             escape,0.0f,maxRight);

    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0),Double.MAX_VALUE);
    cylMoveR.setSchedulingBounds(bounds);


    //Entsprechend fuer die Bewegung von rechts nach links.
    Alpha cylAlphaL = new Alpha(1,2000);
    cylAlphaL.setStartTime(Long.MAX_VALUE);

    PositionInterpolator cylMoveL = new PositionInterpolator(cylAlphaL,tgmCyl,
                                                             escape,maxRight,0.0f);

    cylMoveL.setSchedulingBounds(bounds);


    //Hinzufuegen der Bewegungen zur Transformationsgruppe
    tgmCyl.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    tgmCyl.addChild(cylMoveR);
    tgmCyl.addChild(cylMoveL);


    //Ein Switch, der eine gruene und eine rote Kugel enthalten soll.
    Switch colourSwitch = new Switch();
    colourSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);

    //Eine Appearance fuer die gruene Kugel
    Color3f ambientColourGSphere = new Color3f(0.0f,0.8f,0.0f);
    Color3f emissiveColourGSphere = new Color3f(0.0f,0.0f,0.0f);
    Color3f diffuseColourGSphere = new Color3f(0.0f,0.8f,0.0f);
    Color3f specularColourGSphere = new Color3f(0.0f,0.8f,0.0f);
    float shininessGSphere = 1.0f;

    Appearance greenSphereApp = new Appearance();
    greenSphereApp.setMaterial(new Material(ambientColourGSphere,
                                            emissiveColourGSphere,
                                            diffuseColourGSphere,
                                            specularColourGSphere,
                                            shininessGSphere));

    //Erzeuge die gruene Kugel
    float radius = 0.1f;
    Sphere greenSphere = new Sphere(radius,greenSphereApp);



    //Das gleiche fuer die rote Kugel
    Color3f ambientColourRSphere = new Color3f(0.6f,0.0f,0.0f);
    Color3f emissiveColourRSphere = new Color3f(0.0f,0.0f,0.0f);
    Color3f diffuseColourRSphere = new Color3f(0.6f,0.0f,0.0f);
    Color3f specularColourRSphere = new Color3f(0.8f,0.0f,0.0f);
    float shininessRSphere = 20.0f;

    Appearance redSphereApp = new Appearance();

    redSphereApp.setMaterial(new Material(ambientColourRSphere,emissiveColourRSphere,
                             diffuseColourRSphere,specularColourRSphere,shininessRSphere));

    Sphere redSphere = new Sphere(radius,redSphereApp);



    //Fuege die beiden Kugeln zum Switch
    colourSwitch.addChild(greenSphere);
    colourSwitch.addChild(redSphere);

    //Die CollisionBounds fuer die beiden Kugeln
    colourSwitch.setCollisionBounds(new BoundingSphere(new Point3d(0.0,0.0,0.0),radius));

    //Ermoegliche das Pruefen auf Kollision
    colourSwitch.setCollidable(true);

    //Die gruene Kugel sollte am Anfang sichtbar sein.
    colourSwitch.setWhichChild(0);

    //Eine Transformationsgruppe fuer den Switch (die Kugeln)
    Transform3D tfSphere = new Transform3D();
    tfSphere.setTranslation(new Vector3f(0.7f,0.0f,0.0f));
    TransformGroup tgSphere = new TransformGroup(tfSphere);
    tgSphere.addChild(colourSwitch);



    //Erzeugung des Szenengraphen
    BranchGroup theScene = new BranchGroup();


    //Um das Navigieren mittels Tastatur zu ermoeglichen muss alles
    //in eine separate Transformationsgruppe gepackt werden, auf die
    //das KeyNavigatorBehavior angewendet wird.
    TransformGroup tgAll = new TransformGroup();
    tgAll.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
    tgAll.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    tgAll.addChild(tgBox);
    tgAll.addChild(tgSphere);
    tgAll.addChild(tgmCyl);
    KeyNavigatorBehavior knb = new KeyNavigatorBehavior(tgAll);
    knb.setSchedulingBounds(bounds);
    tgAll.addChild(knb);
    theScene.addChild(tgAll);

    //Das PickTranslateBehavior zur Bewegung des blauen Wuerfels
    PickTranslateBehavior pickTrans = new PickTranslateBehavior(theScene,myCanvas3D,bounds);
    theScene.addChild(pickTrans);



    //Dieses Objekt kuemmert sich um den Farbwechsel der Kugel
    CollisionBehaviour1 scb1 = new CollisionBehaviour1(colourSwitch,bounds);
    theScene.addChild(scb1);


    //Entsprechend ein Objekt fuer die Bewegung(en) des Zylinders
    Alpha[] cylAlphas= new Alpha[2];
    cylAlphas[0] = cylAlphaR;
    cylAlphas[1] = cylAlphaL;

    CollisionBehaviour2 scb2 = new CollisionBehaviour2(cyli,cylAlphas,bounds);
    theScene.addChild(scb2);



    theScene.compile();


    //Hinzufuegen der Szene
    su.addBranchGraph(theScene);

  }






  //Hier wird etwas Licht zu der Szene hinzugefuegt.
  public void addLight(SimpleUniverse su)
  {

    BranchGroup bgLight = new BranchGroup();

    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
    Color3f lightColour1 = new Color3f(1.0f,1.0f,1.0f);
    Vector3f lightDir1  = new Vector3f(-1.0f,0.0f,-0.5f);
    DirectionalLight light1 = new DirectionalLight(lightColour1, lightDir1);
    light1.setInfluencingBounds(bounds);

    bgLight.addChild(light1);


    Vector3f lightDir2  = new Vector3f(1.0f,-1.0f,0.5f);
    DirectionalLight light2 = new DirectionalLight(lightColour1, lightDir2);
    light2.setInfluencingBounds(bounds);

    bgLight.addChild(light2);
    su.addBranchGraph(bgLight);

  }



}
